﻿/**
 * @author: dxl
 * @date:  2017.8.21
 * @brief: 服务器端
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sqlite3.h>

#include "emp_manage.h"


/**
 * @brief 服务器处理登录  ,封装执行结果并回发客户端
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_login(int connfd, MSG *pbuf, sqlite3 *db)
{
	char *errmsg= NULL;
	char **resultp;
	int n_row;
	char sql[256];//命令
	
	sprintf(sql, "select pass,role from t_user where name='%s' ",pbuf->name);
	printf("login selectsql = %s\n", sql);	
	
	if(sqlite3_get_table(db,sql,&resultp,&n_row,NULL, &errmsg) !=  SQLITE_OK)
	{
		 printf("error:%s\n",  errmsg);
		 return ;
	}	
	
	if(n_row == 0){  //登录用户名不存在
		pbuf->opr_result = L_NO_USER;
		send(connfd, pbuf, sizeof(MSG), 0);  
	}
	if(n_row == 1){
		if(strcmp(pbuf->pwd,resultp[2]) == 0){  //登录成功
			pbuf->opr_result = atoi(resultp[3]);
			send(connfd, pbuf, sizeof(MSG), 0);  
		}else{  //登录密码错误
			pbuf->opr_result = L_ERROR_PWD;
			send(connfd, pbuf, sizeof(MSG), 0); 
		}	
	}
}

/**
 * @brief 修改密码
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_update_pwd(int connfd, MSG *pbuf, sqlite3 *db){
	
	char *errmsg;
	char sql[256];//命令
	sprintf(sql, "update t_user set pass='%s' where name='%s' ",pbuf->pwd,pbuf->name);
	printf("update pwd sql = %s\n", sql);	
	
	if(sqlite3_exec(db, sql,  NULL,  NULL, &errmsg) !=  SQLITE_OK)
	{
		printf("error:%s\n",  errmsg);
		pbuf->opr_result = FAIL;
	}else{
		pbuf->opr_result = SUCCESS;
	}
	
	send(connfd, pbuf, sizeof(MSG), 0); 	
}

/**
 * @brief 用户查询自己的信息
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_query_self(int connfd, MSG *pbuf, sqlite3 *db){
	char *errmsg= NULL;
	char **resultp;
	int n_row ,col_num;
	char sql[256];//命令
	
	sprintf(sql, "select role,num,addr,age,salary from t_user where name='%s' ",pbuf->name);
	printf("query self selectsql = %s\n", sql);	
	
	if(sqlite3_get_table(db,sql,&resultp,&n_row,&col_num, &errmsg) !=  SQLITE_OK)
	{
		 printf("error:%s\n",  errmsg);
		 return ;
	}		
	
	pbuf->opr_result = SUCCESS;	
	pbuf->role = atoi(resultp[col_num ]);
	pbuf->num = atoi(resultp[col_num + 1]);
	strcpy(pbuf->addr , resultp[col_num + 2]);
	pbuf->age = atoi(resultp[col_num + 3]);
	pbuf->salary = atof(resultp[col_num + 4]);
	
	send(connfd, pbuf, sizeof(MSG), 0);  
}

/**
 * @brief 添加新用户
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_add_user(int connfd, MSG *pbuf, sqlite3 *db){
	
	char *errmsg;
	char sql[256];//命令
	sprintf(sql, "insert into t_user values('%s','%s',%d,%d,'%s',%d,%.2lf) ",
					pbuf->name,pbuf->pwd,pbuf->role,pbuf->num,pbuf->addr,pbuf->age,pbuf->salary);
	printf("add new user  insertsql = %s\n", sql);	
	
	if(sqlite3_exec(db, sql,  NULL,  NULL, &errmsg) !=  SQLITE_OK)
	{
		printf("error:%s\n",  errmsg);
		pbuf->opr_result = FAIL;
	}else{
		pbuf->opr_result = SUCCESS;
	}
	
	send(connfd, pbuf, sizeof(MSG), 0); 	
}

/**
 * @brief 删除用户
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_delete_user(int connfd, MSG *pbuf, sqlite3 *db){
	
	char *errmsg;
	char sql[256];//命令
	sprintf(sql, "delete from t_user where name='%s' ",pbuf->name);
	printf("sql:::delete user:::deletesql = %s\n", sql);	
	
	if(sqlite3_exec(db, sql,  NULL,  NULL, &errmsg) !=  SQLITE_OK)
	{
		printf("error:%s\n",  errmsg);
		pbuf->opr_result = FAIL;
	}else{
		pbuf->opr_result = SUCCESS;
	}
	
	send(connfd, pbuf, sizeof(MSG), 0); 	
}

/**
 * @brief 根据用户名修改用户信息  员工权限，地址，年龄，工资
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 */
void do_modify_byname(int connfd, MSG *pbuf, sqlite3 *db){
	
	char *errmsg;
	char sql[256] = {0};  //执行sql
	char tmp[50] = {0};  //更新字段临时记录
	
	strcpy(sql,"update t_user set ");
	//判断结构体成员是否为空，不为空时更新
	if(pbuf->role > 0){		
		sprintf(tmp," role=%d,",pbuf->role);
		strcat(sql,tmp);
	}
	if( pbuf->addr != NULL){		
		sprintf(tmp," addr='%s',",pbuf->addr);
		strcat(sql,tmp);
	}
	if(pbuf->age > 0){		
		sprintf(tmp," age=%d,",pbuf->age);
		strcat(sql,tmp);
	}
	if(pbuf->salary > 0){		
		sprintf(tmp," salary=%lf,",pbuf->salary);
		strcat(sql,tmp);
	}
		
	//去除sqltmp最后的逗号
	sql[strlen(sql) - 1] = '\0';	
	
	//添加where条件
	sprintf(tmp," where name='%s' ",pbuf->name);
	strcat(sql,tmp);
	
	printf("sql:::modify user info:::updatesql = %s\n", sql);	
	
	if(sqlite3_exec(db, sql,  NULL,  NULL, &errmsg) !=  SQLITE_OK)
	{
		printf("error:%s\n",  errmsg);
		pbuf->opr_result = FAIL;
	}else{
		pbuf->opr_result = SUCCESS;
	}
	
	send(connfd, pbuf, sizeof(MSG), 0); 	
}


/**
 * @brief 根据用户名查询用户信息  员工权限，编号，地址，年龄，工资
 * @param connfd 客户端fd
 * @param pbuf 信息结构体指针
 * @param db 数据库句柄
 *   警告。。。。。：和do_query_self相同了。。。。。。。。
 */
void do_query_byname(int connfd, MSG *pbuf, sqlite3 *db){
	char *errmsg= NULL;
	char **resultp;
	int n_row ,col_num;
	char sql[256];//命令
	
	sprintf(sql, "select role,num,addr,age,salary from t_user where name='%s' ",pbuf->name);
	printf("sql:::manager query user info by user name:::sql = %s\n", sql);	
	
	if(sqlite3_get_table(db,sql,&resultp,&n_row,&col_num, &errmsg) !=  SQLITE_OK)
	{
		 printf("error:%s\n",  errmsg);
		 return ;
	}		
	
	pbuf->opr_result = SUCCESS;	
	pbuf->role = atoi(resultp[col_num ]);
	pbuf->num = atoi(resultp[col_num + 1]);
	strcpy(pbuf->addr , resultp[col_num + 2]);
	pbuf->age = atoi(resultp[col_num + 3]);
	pbuf->salary = atof(resultp[col_num + 4]);
	
	send(connfd, pbuf, sizeof(MSG), 0);  
}

/**
 * @brief 处理客户端请求
 * @param fd 客户端fd
 * @param db 数据库句柄
 */
void do_client(int connfd, sqlite3 *db)
{
	MSG buf;
	while(1){
		printf("waiting for  recv...\n");
		int ret = recv(connfd,&buf,sizeof(buf),0);
		if( ret > 0){
			switch(buf.opr_type){
				case L:
					printf("will login\n");
					do_login(connfd, &buf, db);
					break; 
				//普通员工操作
				case M_PWD:
					printf("will update password\n");	
					do_update_pwd(connfd, &buf, db);				
					break; 
				case Q:
					printf("will query self info\n");	
					do_query_self(connfd, &buf, db);						
					break;
				//管理员操作
				case A:
					printf("will add new user\n");	
					do_add_user(connfd, &buf, db);	
					break;
				case D:
					printf("will delete user\n");	
					do_delete_user(connfd, &buf, db);
					break;
				case M_USER:
					printf("will modify user info\n");	
					do_modify_byname(connfd, &buf, db);
					break;
				case QA:
					printf("will list user info by user name\n");		
					do_query_byname(connfd, &buf, db);
					break;
				default:
					
					break;
				
			}			
		}else if(ret == 0){  //客户端退出
			printf("client quit,wait for child process  to close\n");
			return;  //退出 do_client ，返回子进程调用处
		}else{
			perror("recv error...\n");		
		}
	}
}

void do_signal(int signo)
{	
	printf("get signal SIGCHLD,exec  wait(NULL)\n");
    wait(NULL);  //回收资源	
}

int main(int argc,char *argv[]){
	
	if(argc!=3){
		printf("Usage : %s <ip> <port>\n", argv[0]);
		return -1;		
	}
	
	//打开数据库
	sqlite3 *db;
	if(sqlite3_open(DATABASE,&db) < 0){
		printf("fail to sqlite3_open : %s\n", sqlite3_errmsg(db));
        return -1;		
	}
	
	//socket监听网络
	//变量
	int listenfd,connfd;
	struct sockaddr_in server_addr;
	pid_t pid;
	MSG buf;
	
	//创建服务器socket
	listenfd = socket(PF_INET,SOCK_STREAM,0);
	if(listenfd < 0){
		perror("fail to socket");
		return -1;		
	}
	
	//填充地址结构体
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	
	//绑定服务器地址
	if( bind(listenfd,(struct sockaddr *)&server_addr,sizeof(server_addr) ) < 0){
		perror("fail to bind");
        exit(-1);		
	}
	
	//监听
	if( listen(listenfd,5) <0){
		perror("fail to listen");
        exit(-1);		
	}
	
	//signal(SIGCHLD,do_signal);  //回收子进程
	
	//并发服务器模型,实现多用户登录
	while(1){
		//有用户接入
		//printf("usr is comming\n");
		
		if( (connfd = accept(listenfd,NULL,NULL)) < 0){
			perror("fail to accept");
			return -1;
		}
		
		pid = fork();
		if(pid == -1){
            perror("fail to fork\n");
            return -1;
        }
        else if(pid == 0){ //子进程 处理接入的客户端
			//close(listenfd);
			
            printf("a client comming\n");
            do_client(connfd, db);			
			close(connfd);  //do_client执行完毕，关闭connfd
			
			printf("child process closes...\n");
			exit(0);
        }else{  //父进程  继续监听服务器
			printf("子进程号：%d\n",pid);
            close(connfd);
        }
		
	}
	
	return 0;
}